<?php

/**
 * AWS S3 Version 4签名类
 * 参考:
 * https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
 * https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/archive-sig-v4-header-based-auth.html
 */
class AwsSignatureV4 {
	static $region = "us-east-1";
	static $service = "s3";
	static $LINE_SEPARATOR = "\n";
	
	// 生成s3v4签名
	function createSign($endpoint, $bucket, $accessKey, $secretKey, $fileResource, $httpMethod, $date, $dateTime, $queryParams, $headers) {
		$canonicalRequest = self::createCanonicalRequest ( $fileResource, $httpMethod, $queryParams, $headers );
		$stringToSign = self::createStringToSign ( $date, $dateTime, $canonicalRequest );
		$signingKey = self::createSigningKey ( $date, $secretKey );
		$sign = bin2hex ( self::HMACSHA256 ( $stringToSign, $signingKey ) );
		return $sign;
	}
	
	function createCanonicalRequest($resource, $httpMethod, $queryParams, $headers) {
		$httpVerb = $httpMethod;
		$canonicalUri = "/";
		if (self::startwith ( $resource, "/" )) { // 判断资源的路径是以/开头，那么canonicalUri就不要再次拼接/
			$canonicalUri = "";
		}
		$canonicalUri = $canonicalUri . $resource;
		// $canonicalQueryString，要遍历$queryParams
		$canonicalQueryString = "";
		foreach ( $queryParams as $key => $value ) {
			if ($canonicalQueryString != "") {
				$canonicalQueryString = $canonicalQueryString . "&";
			}
			$canonicalQueryString = $canonicalQueryString . $key . "=" . $value;
		}
		// $canonicalHeaders，要遍历$headers
		$canonicalHeaders = "";
		foreach ( $headers as $key => $value ) {
			$headerName = strtolower ( $key );
			$canonicalHeaders = $canonicalHeaders . $headerName . ":" . $value . self::$LINE_SEPARATOR;
		}
		$signedHeaders = self::getSignedHeaders($headers);
		$unsignedPayload = "UNSIGNED-PAYLOAD";
		// 拼接canonicalRequest
		$canonicalRequest = $httpVerb . self::$LINE_SEPARATOR . $canonicalUri . self::$LINE_SEPARATOR . $canonicalQueryString . self::$LINE_SEPARATOR . $canonicalHeaders . self::$LINE_SEPARATOR . $signedHeaders . self::$LINE_SEPARATOR . $unsignedPayload;
		return $canonicalRequest;
	}
	
	function getSignedHeaders($headers){
		$signedHeaders = "";
		//$signedHeaders，要遍历$headers
		foreach ( $headers as $key => $value ) {
			$headerName = strtolower ( $key );
			if ($signedHeaders != "") {
				$signedHeaders = $signedHeaders . ";";
			}
			$signedHeaders = $signedHeaders . $headerName;
		}
		return $signedHeaders;
	}
		
	function createStringToSign($date, $dateTime, $canonicalRequest) {
		// 对canonicalRequest进行编码
		$encodeCanonicalRequest = self::SHA256Hex ( $canonicalRequest );
		$scope = $date . "/" . self::$region . "/" . self::$service . "/aws4_request";
		$stringToSign = "AWS4-HMAC-SHA256" . self::$LINE_SEPARATOR . $dateTime . self::$LINE_SEPARATOR . $scope . self::$LINE_SEPARATOR . $encodeCanonicalRequest;
		return $stringToSign;
	}
	
	function createSigningKey($date, $secretKey) {
		$kDate = self::HMACSHA256 ( $date, "AWS4" . $secretKey );
		$kRegion = self::HMACSHA256 ( self::$region, $kDate );
		$kService = self::HMACSHA256 ( self::$service, $kRegion );
		$signingKey = self::HMACSHA256 ( "aws4_request", $kService );
		return $signingKey;
	}
	
	/**
	 * 生成请求头Authorization
	 * @param unknown $accessKey
	 * @param unknown $date
	 * @param unknown $dateTime
	 * @param unknown $headers
	 * @param unknown $sign
	 * @return string
	 */
	function createAuthorizationHeader($accessKey, $date, $headers, $sign) {
		$credential = $accessKey . "/" . $date . "/" . self::$region . "/" . self::$service . "/" . "aws4_request";
		$signedHeaders = self::getSignedHeaders ( $headers );
		$Authorization = "AWS4-HMAC-SHA256 Credential=" . $credential . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $sign;
		return $Authorization;
	}
	
	
	/**
	 * 解析endpoint
	 *
	 * @param unknown $endpoint
	 * @param unknown $bucket
	 * @param unknown $type 解析方式：h代表解析出host；p代表解析出requestPath，  结构：scheme://[bucket].[host][path]
	 */
	function resolveEndpoint($endpoint, $bucket, $type) {
		$result = "";
		$requestInfo = parse_url ( $endpoint );
		$scheme = $requestInfo ["scheme"];
		$host = $requestInfo ["host"];
		$newHost = $bucket . "." . $host;
		if ("h" == $type) {
			$result = $newHost;
		} else if ("p" == $type) {
			$path = "";
			if (in_array ( "path", $requestInfo )) {
				$path = $requestInfo ["path"];
			}
			if ($path != "" && self::endWith ( $path, "/" )) { // 如果path以/结尾，要把/去掉
				$path = substr ( $path, 0, strlen ( $path ) - 1 );
			}
			$result = $scheme . "://" . $newHost . $path;
		}
		return $result;
	}
	
	
	function HMACSHA256($data, $key) {
		return hash_hmac ( "sha256", $data, $key, true );
	}
	
	function SHA256Hex($str) {
		$re = hash ( 'sha256', $str, true );
		return bin2hex ( $re );
	}
	
	/**
	 * 返回日期，格式：yyyymmdd
	 * @return string
	 */
	function getDate(){
		return gmdate ( "Ymd" );
	}
	
	/**
	 * 返回时间，格式：yyyymmddTHHmissZ，例如：20200501T104105Z
	 * @return string
	 */
	function getDateTime(){
		return gmdate ( "Ymd\THis" ) . "Z";
	}
	
	/**
	 * 判断字符串开头
	 * 
	 * @param unknown $str        	
	 * @param unknown $pattern        	
	 * @return boolean
	 */
	function startwith($str, $pattern) {
		if (strpos ( $str, $pattern ) === 0)
			return true;
		else
			return false;
	}
	
	/**
	 * 判断字符串的结尾
	 * @param unknown $haystack  原串
	 * @param unknown $needle    部分串
	 * @return boolean
	 */
	function endWith($haystack, $needle) {
		$length = strlen ( $needle );
		if ($length == 0) {
			return true;
		}
		return (substr ( $haystack, - $length ) === $needle);
	}
}

?>
    
   
    